package net

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"mime/multipart"
	"net/http"
	"time"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/gclient"
	"github.com/gogf/gf/v2/util/gconv"

	"github.com/r3labs/sse/v2"
)

const (
	httpHeaderContentTypeJson       = `application/json`
	httpHeaderContentTypeXml        = `application/xml`
	httpHeaderContentTypeUrlEncoded = `application/x-www-form-urlencoded`
)

func NewHttp(ctx context.Context) *http2 {
	r := &http2{
		ctx:     ctx,
		client:  g.Client(),
		timeout: time.Second * 10,
		header:  make(map[string]string),
	}

	r.SetAgent(NewHelper().GetUserAgent())
	r.setRealIpHeader()

	return r
}

type http2 struct {
	ctx     context.Context
	client  *gclient.Client
	timeout time.Duration
	header  map[string]string
	retry   int
}

func (r *http2) SetTimeout(seconds uint) *http2 {
	r.client.SetTimeout(time.Second * time.Duration(seconds))

	return r
}
func (r *http2) SetRetry(retry int, intervals ...time.Duration) *http2 {
	interval := time.Second
	if len(intervals) > 0 {
		interval = intervals[0]
	}

	r.client.SetRetry(retry, interval)

	return r
}

func (r *http2) SetHeader(k, v string) *http2 {
	r.client.SetHeader(k, v)

	r.header[k] = v

	return r
}
func (r *http2) SetHeaders(header map[string]string) *http2 {
	for k, v := range header {
		r.SetHeader(k, v)
	}

	return r
}
func (r *http2) SetAgent(agent string) *http2 {
	r.client.SetAgent(agent)

	return r
}
func (r *http2) setRealIpHeader() {
	ip := gconv.String(r.ctx.Value("client_ip"))
	if ip != "" {
		r.client.SetHeader("X-Forwarded-For", ip)
		r.client.SetHeader("X-Real-IP", ip)
	}
	// req.GetClientIp()
	// X-Forwarded-For
	// Ali-CDN-Real-IP
}

func (r *http2) SetContentTypeJson() *http2 {
	r.client.ContentType(httpHeaderContentTypeJson)

	return r
}
func (r *http2) SetContentTypeXml() *http2 {
	r.client.ContentType(httpHeaderContentTypeXml)

	return r
}
func (r *http2) SetContentTypeUrlEncoded() *http2 {
	r.client.ContentType(httpHeaderContentTypeUrlEncoded)

	return r
}
func (r *http2) SetContentTypeMultipartData() *http2 { // multipart/form-data
	writer := multipart.NewWriter(&bytes.Buffer{})

	r.client.ContentType(writer.FormDataContentType())

	return r
}

func (r *http2) Request(method, url string, data ...interface{}) string {
	response, err := r.client.DoRequest(r.ctx, method, url, data...)
	if err != nil {
		panic(err)
	}

	return response.ReadAllString()
}
func (r *http2) Get(url string, data ...interface{}) string {
	return r.Request(http.MethodGet, url, data...)
}
func (r *http2) Post(url string, data ...interface{}) string {
	return r.Request(http.MethodPost, url, data...)
}
func (r *http2) Put(url string, data ...interface{}) string {
	return r.Request(http.MethodPut, url, data...)
}
func (r *http2) Delete(url string, data ...interface{}) string {
	return r.Request(http.MethodDelete, url, data...)
}

func (r *http2) DoRequest(method, url string, data ...interface{}) []byte {
	response, err := r.client.DoRequest(r.ctx, method, url, data...)
	if err != nil {
		panic(err)
	}

	return response.ReadAll()
}
func (r *http2) DoGet(url string, data ...interface{}) (*gclient.Response, string, error) {
	response, err := r.client.Get(r.ctx, url, data...)

	return r.getResponse(response, err)
}
func (r *http2) DoPost(url string, data ...interface{}) (*gclient.Response, string, error) {
	response, err := r.client.Post(r.ctx, url, data...)

	return r.getResponse(response, err)
}
func (r *http2) DoPut(url string, data ...interface{}) (*gclient.Response, string, error) {
	response, err := r.client.Put(r.ctx, url, data...)

	return r.getResponse(response, err)
}
func (r *http2) DoDelete(url string, data ...interface{}) (*gclient.Response, string, error) {
	response, err := r.client.Delete(r.ctx, url, data...)

	return r.getResponse(response, err)
}

func (r *http2) Sse(url string, handler func(msg *sse.Event)) error {
	sseClient := sse.NewClient(url)
	for k, v := range r.header {
		sseClient.Headers[k] = v
	}
	return sseClient.SubscribeRaw(handler)
}

// func httpHandler(w http2.ResponseWriter, req *http2.Request) {
// 	err := sse.Encode(w, sse.Event{
// 		Id:    "124",
// 		Event: "message",
// 		// Data:  "some data\nmore data",
// 		Data: map[string]interface{}{
// 			"user":    "beesoft",
// 			"date":    time.Now().Unix(),
// 			"content": "hi!",
// 		},
// 	})
// 	if err != nil {
// 		panic(err)
// 	}
//
// 	// sse.ContentType
// }

func (r *http2) getResponse(response *gclient.Response, err error) (*gclient.Response, string, error) {
	body := ""

	if response != nil {
		defer func() {
			err := response.Close()
			if err != nil {
				panic(err)
			}
		}()

		body = response.ReadAllString()

		if err == nil && !(response.StatusCode >= 200 && response.StatusCode < 300) {
			return response, body, errors.New(fmt.Sprintf("invalid response, the http2 status code is %d", response.StatusCode))
		}
	}

	return response, body, err
}
